package org.oimwrapper.connectors.internal;

import Thor.API.*;
import Thor.API.Exceptions.tcAPIException;
import Thor.API.Exceptions.tcColumnNotFoundException;
import Thor.API.Exceptions.tcInvalidUpdateException;
import Thor.API.Exceptions.tcRequestInvalidException;
import Thor.API.Operations.*;
import com.thortech.util.logging.*;
import com.thortech.xl.dataaccess.*;
import java.util.*;

import org.oimwrapper.connectors.*;

/**
 * This provides common functions for managing requests.
 * 
 * THIS IS AN INTERNAL CLASS AND SHOULD NOT BE USED DIRECTLY.
 */
public final class OIMRequestUtilities {

    /**
     * The default logger instance for this instance.
     */
    private Logger logger;
    /**
     * The object operations instance that backs this object.
     */
    private tcObjectOperationsIntf objectOp;
    /**
     * The request operations instance that backs this object.
     */
    private tcRequestOperationsIntf requestOp;
    /**
     * The form instance operations instance that backs this object.
     */
    private tcFormInstanceOperationsIntf formInstOp;
    /**
     * The user operations instance that backs this object.
     */
    private tcUserOperationsIntf userOp;
    /**
     * The data provider used by this object.
     */
    private tcDataProvider database;

    private static final String[] requestFields = {
        "Process Instance.Key",
        "RequestAdmin.First Name",
        "RequestAdmin.Key",
        "RequestAdmin.Last Name",
        "RequestAdmin.User ID",
        "Requester.First Name",
        "Requester.Last Name",
        "Requester.User ID",
        "Requester.Manager Key",
        "Requester.Manager First Name",
        "Requester.Manager Last Name",
        "Requester.Manager User ID",
        "Requests.Created By",
        "Requests.Creation Date",
        "Requests.Key",
        "Requests.Note",
        "Requests.Object Request Type",
        "Requests.Request ID",
        "Requests.Request Priority",
        "Requests.Row Version",
        "Requests.Target Type",
        "Requests.Type",
        "Requests.Update Date",
        "Requests.Updated By",
        "Requests.Updated By First Name",
        "Requests.Updated By Last Name",
        "Requests.Updated By Login"
    };
    
    /**
     * The default constructor.
     *
     * @param ClassLogger The Logger used by the invoking class.
     * @param ObjectOp The object operations utility from the invoking class.
     * @param RequestOp The request operations utility from the invoking class.
     * @param FormInstOp The form instance operations utility from the invoking class.
     * @param UserOp The user operations utility from the invoking class.
     * @param Database The data provider from the invoking class.
     */
    protected OIMRequestUtilities(Logger ClassLogger, tcObjectOperationsIntf ObjectOp, tcRequestOperationsIntf RequestOp, tcFormInstanceOperationsIntf FormInstOp, tcUserOperationsIntf UserOp, tcDataProvider Database) {
        logger = ClassLogger;
        objectOp = ObjectOp;
        requestOp = RequestOp;
        formInstOp = FormInstOp;
        userOp = UserOp;
        database = Database;
    }

    /**
     * Gets the instance key for a user, or 0 if the user is not found.
     *
     * @param Name The name of the user to get the instance key for.
     * @return The instance key of the user, or 0 if the user is not found.
     * @exception Exception
     */
    public long getUser(String Name) throws Exception {
        logger.debug("Entering requestUtilities.getUser()");
        long result = 0L;
        Map searchFor = new HashMap();
        searchFor.put("Users.User ID", Name == null ? "" : Name.toUpperCase());
        tcResultSet results = userOp.findUsers(searchFor);
        if (results.getRowCount() == 1) {
            results.goToRow(0);
            result = results.getLongValue("Users.Key");
        }
        logger.debug("Exiting requestUtilities.getUser()");
        return result;
    }

    /**
     * Executes the prepop adapters for a given form and returns the resulting data in a Map of
     * attributes and values.
     *
     * @param ObjectKey The key of the object being prepopulated.
     * @param ObjectInstanceKey The instance key for the process associated with the form.
     * @param RequesterKey The key of the requester you the prepop adapters should be run as.
     * @return A Map with the data resulting from the executing of the prepopulate adapters.
     * @exception Exception
     */
    protected Map getPrepopData(long ObjectKey, long ObjectInstanceKey, long RequesterKey) throws Exception {
        logger.debug("Entering requestUtilities.getPrepopData()");
        Map result = new HashMap();
        Map searchFor = new HashMap();
        searchFor.put("Objects.Key", Long.toString(ObjectKey));
        tcResultSet results = objectOp.findObjects(searchFor);
        if (results.getRowCount() == 1) {
            results.goToRow(0);
            if (results.getStringValue("Objects.Auto Prepopulate").equals("1")) {
                long formDefKey = formInstOp.getObjectFormDefinitionKey(ObjectInstanceKey);
                results = formInstOp.prepopulateObjectForm(RequesterKey, formDefKey, new HashMap());
                if (results.getRowCount() == 1) {
                    results.goToRow(0);
                    String[] columns = results.getColumnNames();
                    for (int i = 0; i < columns.length; i++) {
                        result.put(columns[i], results.getStringValue(columns[i]));
                    }
                }
            }
        }
        logger.debug("Exiting requestUtilities.getPrepopData()");
        return result;
    }

    /**
     * Gets the child form definition key for a given child table.
     *
     * @param ObjectInstanceKey The instance key for the object associated with the form.
     * @param ChildTable The name of the child table to retrieve data for.
     * @return The key of the child table, or 0 if the child table is not found.
     * @exception Exception
     */
    protected long getChildFormDefKey(long ObjectInstanceKey, String ChildTable) throws Exception {
        logger.debug("Entering requestUtilities.getChildFormDefKey()");
        long result = 0L;
        int formVersion = getObjectFormVersion(ObjectInstanceKey);
        long formDefKey = formInstOp.getObjectFormDefinitionKey(ObjectInstanceKey);
        tcResultSet childFormDefs = formInstOp.getChildFormDefinition(formDefKey, formVersion);
        for (int i = 0; i < childFormDefs.getRowCount(); i++) {
            childFormDefs.goToRow(i);
            if (childFormDefs.getStringValue("Structure Utility.Table Name").equalsIgnoreCase(ChildTable)) {
                result = childFormDefs.getLongValue("Structure Utility.Child Tables.Child Key");
                break;
            }
        }
        logger.debug("Exiting requestUtilities.getChildFormDefKey()");
        return result;
    }
    
    private int getObjectFormVersion(long ObjectInstanceKey) throws Exception
    {
        logger.debug("Entering formUtilities.getChildTables()");
        int formVersion = 0;
        try
        {
            formVersion = formInstOp.getObjectFormVersion(ObjectInstanceKey);
            return formVersion;
        }
        catch(tcAPIException e)
        {
            logger.error("API Failed:getObjectFormVersion",e);
            throw e;
        }
        catch(Exception e)
        {
            logger.info("Failed to find Object Version. Trying Slow Approach:" + e.getMessage());
        }
        
        try
        {
            long objectFormDefKey = formInstOp.getObjectFormDefinitionKey(ObjectInstanceKey);
            formVersion = formInstOp.getActiveVersion(objectFormDefKey);
            return formVersion;
        }
        catch(tcAPIException e)
        {
            logger.error("API Failed:getObjectFormVersion slow",e);
            throw e;
        }
        catch(Exception e)
        {
            logger.info("Failed to find Object Version. With Slow Approach:" + e.getMessage());
        }
        throw new Exception("Failed to find Object Version:" + ObjectInstanceKey);
    }

    /**
     * Creates a new request in OIM.
     *
     * @param Request The data for the request to create.
     * @return The key of the resulting request.
     * @exception Exception
     */
    public long createRequest(OIMRequest Request) throws Exception {
        return createRequest(Request, getUser(database.getUserLogin()));
    }

    /**
     * Creates a new request in OIM.
     *
     * @param Request The data for the request to create.
     * @param RequesterKey The key of the requester you the prepop adapters should be run as.
     * @return The key of the resulting request.
     * @exception Exception
     */
    public long createRequest(OIMRequest Request, long RequesterKey) throws Exception {
        logger.debug("Entering requestUtilities.createRequest()");
        Map info = Request.getRequestInfo();
        long requestKey = requestOp.createRequest(info);
        if (Request.forUser()) {
            for (Iterator iterator = Request.getTargets().iterator(); iterator.hasNext();) {
                long userKey = ((Long) iterator.next()).longValue();
                requestOp.addRequestUser(requestKey, userKey);
            }
        } else if (Request.forOrg()) {
            for (Iterator iterator = Request.getTargets().iterator(); iterator.hasNext();) {
                long orgKey = ((Long) iterator.next()).longValue();
                requestOp.addRequestOrganization(requestKey, orgKey);
            }
        }
        Map objects = Request.getObjects();
        for (Iterator iterator = objects.keySet().iterator(); iterator.hasNext();) {
            Long objectKey = (Long) iterator.next();
            OIMFormData[] objectData = (OIMFormData[]) ((List) objects.get(objectKey)).toArray(new OIMFormData[0]);
            for (int i = 0; i < objectData.length; i++) {
                requestOp.addRequestObject(requestKey, objectKey.longValue(), false);
            }
            tcResultSet instances = requestOp.getRequestObjects(requestKey);
            int dataCount = 0;
            for (int i = 0; i < instances.getRowCount(); i++) {
                instances.goToRow(i);
                if (objectKey.longValue() == instances.getLongValue("Objects.Key")) {
                    long objectInstanceKey = instances.getLongValue("Object Instance.Key");
                    Map parentData = getPrepopData(objectKey.longValue(), objectInstanceKey, RequesterKey);
                    parentData.putAll(objectData[dataCount].getParentData());
                    logger.debug("Setting Parent Data:" + parentData);
                    formInstOp.setObjectFormData(objectInstanceKey, parentData);
                    String[] childTables = objectData[dataCount].getChildTables();
                    for (int j = 0; j < childTables.length; j++) {
                        logger.debug("Process Child:" + childTables[j]);
                        long childTableDefKey = getChildFormDefKey(objectInstanceKey, childTables[j]);
                        Map[] childRecords = objectData[dataCount].getChildData(childTables[j]);
                        for (int k = 0; k < childRecords.length; k++) {
                            logger.debug("ChildData:" + childRecords[k]);
                            formInstOp.addObjectFormChildData(childTableDefKey, objectInstanceKey, childRecords[k]);
                        }
                    }
                    dataCount++;
                }
            }
        }
        requestOp.completeRequestCreation(requestKey);
        logger.debug("Exiting requestUtilities.createRequest()");
        return requestKey;
    }

    public void updateRequest(long requestKey, Map attributes) throws Exception {


        try
        {
            requestOp.updateRequest(requestKey, attributes);
        }
        catch(tcAPIException ex)
        {
            logger.error("tcAPIException",ex);
            throw new Exception("tcAPIException",ex);
        }
        catch(tcRequestInvalidException ex)
        {
            logger.error("tcRequestInvalidException",ex);
            throw new Exception("tcRequestInvalidException",ex);

        }
        catch(tcInvalidUpdateException ex)
        {
            logger.error("tcInvalidUpdateException",ex);
            throw new Exception("tcInvalidUpdateException",ex);
        }
    }

    public Map getRequest(long requestId) throws Exception
    {
        Map returnMap = new HashMap();
        try
        {
            Map attributes = new HashMap();
            attributes.put("Requests.Key",Long.toString(requestId));
            tcResultSet rs = requestOp.findRequests(attributes);
            if (rs.getRowCount() > 0)
            {
                int totrows = rs.getRowCount();
                for(int i=0;i<totrows;i++)
                {
                    rs.goToRow(i);
                    for(String fldName : requestFields)
                    {
                        String val = rs.getStringValue(fldName);
                        returnMap.put(fldName, val);
                    }
                }
            }

            return returnMap;
        }
        catch(tcAPIException ex)
        {
            logger.error("tcAPIException",ex);
            throw new Exception("tcAPIException",ex);
        }
        catch(tcColumnNotFoundException ex)
        {
            logger.error("tcColumnNotFoundException",ex);
            throw new Exception("tcColumnNotFoundException",ex);
        }

    }
}
